#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>

template<class T>
class BlockQueue
{
private:
    bool IsFull()
    {
        return _blockQueue.size() == _cap;
    }
    bool IsEmpty()
    {
        return _blockQueue.empty();
    }
public:
    BlockQueue(int cap)
        :_cap(cap)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_productCond, nullptr);
        pthread_cond_init(&_consumCond, nullptr);
    }
    void Enqueue(T& in)  //生产者
    {
        pthread_mutex_lock(&_mutex);

        if(IsFull()) //访问公共资源，需要加锁
        {
            //如果满了，就在指定条件变量下等待， 1.将线程放到指定条件变量下等待 2.释放当前持有锁，这时不能带着锁去等待
            pthread_cond_wait(&_productCond, &_mutex);
        }
        //还有空间
        _blockQueue.emplace(in);
        //通知消费者去消费
        pthread_cond_signal(&_consumCond);

        pthread_mutex_unlock(&_mutex);
    }
    void pop(T* out) //消费者
    {
        pthread_mutex_lock(&_mutex);
        if(IsEmpty())
        {
            pthread_cond_wait(&_consumCond, &_mutex);
        }

        *out = _blockQueue.front();
        _blockQueue.pop();
        //通知生产者去生产
        pthread_cond_signal(&_productCond);
        pthread_mutex_unlock(&_mutex);
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_productCond);
        pthread_cond_destroy(&_consumCond);
    }
private:
    std::queue<T> _blockQueue;
    int _cap;
    
    pthread_mutex_t _mutex;
    pthread_cond_t _productCond; //生产者条件变量
    pthread_cond_t _consumCond; //消费者条件变量

};